/****************************************************************
irrEventManager
Copyright (C) 2007-2008 Keith Holbrook
This file is part of "irrEventManager".
For conditions of distribution and use, see copyright notice in irrEventManager.h

*****************************************************************/

#include "CEventManager.h"

#pragma warning(disable: 4800)

namespace irr
{
#ifdef __cplusplus
    extern "C"
#endif

    _IRREVENTMANAGER_API irr::IEventManager* _IRREVENTMANAGER_CALLCONV  createEventManager()
    {
        return new CEventManager();
    }


    CEventManager::CEventManager() :
            wheel(0),wheelFlag(0)
            ,delay(50),dt(0),chr(0)
            ,lastKeyup(0)
            ,KeyEvent(0)
            ,MouseEvent(0)
            ,LogEvent(0)
            ,UserEvent(0)
            ,JoyEvent(0)


    {

        memset(mouseKeys,0,3);
        memset(Keys, 0, irr::KEY_KEY_CODES_COUNT);

#ifndef _IRREVENTMANAGER_USEBITS
        memset(mouseKeysOld,0,3);
        memset(mouseKeysOld2,0,3);
        memset(KeysOld, 0, irr::KEY_KEY_CODES_COUNT);
        memset(KeysOld2, 0, irr::KEY_KEY_CODES_COUNT);
        lastMouseUp =-1;
#endif
//        GuiEvents.reallocate(guiEvents.size());
//        GuiEvents = new CGuiEventEx*[guiEvents.size()];
//        for (u32 i=0;i<guiEvents.size();i++)
//            GuiEvents[i]=0;
#ifdef _DEBUG
        DEBUG_PRINT("EventManager  created\n");
#endif


    }

    void CEventManager::removeAll()
    {
        //        if (!guiEvents.empty())
//        {
           for (u32 i=0; i< guiEvents.size();i++)
                if (guiEvents[i])
                    delete guiEvents[i];
             guiEvents.clear();
            // delete []GuiEvents;
//        }
        delete KeyEvent;
        delete MouseEvent;
        delete UserEvent;
        delete LogEvent;
        delete JoyEvent;
        KeyEvent=0;
        MouseEvent=0;
        UserEvent=0;
        LogEvent=0;
        JoyEvent=0;

    }

    CEventManager::~CEventManager()
    {
        removeAll();

#ifdef _DEBUG
        DEBUG_PRINT("EventManager  destroyed\n");
#endif
    }


    bool CEventManager::OnEvent(const irr::SEvent& event)
    {
         _IRR_DEBUG_BREAK_IF(event.EventType == EGUIET_FORCE_32_BIT)
        switch (event.EventType)
        {
        case irr::EET_KEY_INPUT_EVENT:

#ifndef _IRREVENTMANAGER_USEBITS
            Keys[event.KeyInput.Key] = event.KeyInput.PressedDown;
#endif
            if (event.KeyInput.PressedDown)
            {
#ifdef _IRREVENTMANAGER_USEBITS
                Keys[event.KeyInput.Key] |= 1;
#endif
                chr = event.KeyInput.Char;
            }
            else
            {
#ifdef _IRREVENTMANAGER_USEBITS
                Keys[event.KeyInput.Key] &= ~1;
#endif
                chr =0;
                dt =0;
                lastKeyup = event.KeyInput.Key;
            }

#ifdef _IRREVENTMANAGER_USEBITS
            event.KeyInput.Shift ? Keys[irr::KEY_SHIFT] |= 1: Keys[irr::KEY_SHIFT]   &= ~1;
            event.KeyInput.Control ? Keys[irr::KEY_CONTROL] |= 1: Keys[irr::KEY_CONTROL] &= ~1;

#ifdef USE_CUSTOM_SEVENT
            event.KeyInput.LShift ? Keys[irr::KEY_LSHIFT]  |= 1 : Keys[irr::KEY_LSHIFT]  &= ~1;
            event.KeyInput.RShift ? Keys[irr::KEY_RSHIFT]  |= 1 : Keys[irr::KEY_RSHIFT]  &= ~1;
            event.KeyInput.LControl ? Keys[irr::KEY_LCONTROL]  |= 1 : Keys[irr::KEY_LCONTROL]  &= ~1;
            event.KeyInput.RControl ? Keys[irr::KEY_RCONTROL]  |= 1 : Keys[irr::KEY_RCONTROL]  &= ~1;

#endif
#else   //_IRREVENTMANAGER_USEBITS
            Keys[irr::KEY_SHIFT] = event.KeyInput.Shift;
            Keys[irr::KEY_CONTROL] = event.KeyInput.Control;

#ifdef USE_CUSTOM_SEVENT
             Keys[irr::KEY_LSHIFT]   = event.KeyInput.LShift;
             Keys[irr::KEY_RSHIFT]   = event.KeyInput.RShift;
             Keys[irr::KEY_LCONTROL] = event.KeyInput.LControl;
             Keys[irr::KEY_RCONTROL] = event.KeyInput.RControl;

#endif
#endif
            if (KeyEvent)return KeyEvent->doEvent(event);
            return false;
        case irr::EET_MOUSE_INPUT_EVENT:
            //mouseKeys[event.MouseInput.Event] &= ~1;
            if (event.MouseInput.Event <= irr::EMIE_MMOUSE_PRESSED_DOWN)
            {
#ifdef _IRREVENTMANAGER_USEBITS
                mouseKeys[event.MouseInput.Event] |= 1;
#else
                mouseKeys[event.MouseInput.Event] = 1;
#endif
                //m_mouse_button_buf_old[event.MouseInput.Event%3] = m_mouse_button_buf[event.MouseInput.Event%3];
                // m_mouse_button_buf[event.MouseInput.Event%3] = ((event.MouseInput.Event/3)?false:true);
            }
            else
                if (event.MouseInput.Event <= irr::EMIE_MMOUSE_LEFT_UP)
                {
#ifdef _IRREVENTMANAGER_USEBITS
                    mouseKeys[event.MouseInput.Event%3] |= (1<<4);
                    mouseKeys[event.MouseInput.Event%3] &= ~1;
#else
                    mouseKeys[event.MouseInput.Event%3] = 0;
                    lastMouseUp=(EMOUSE_BUTTONS)(event.MouseInput.Event%3);
#endif

                }
                else
                    if (event.MouseInput.Event == EMIE_MOUSE_MOVED)
                    {
                        msePos.X = event.MouseInput.X;
                        msePos.Y = event.MouseInput.Y;
                    }
                    else
                        if (event.MouseInput.Event == EMIE_MOUSE_WHEEL)
                        {
                            wheel = event.MouseInput.Wheel;
                            wheelFlag = 1;
                        }
            if (MouseEvent) return  MouseEvent->doEvent(event);
            return false;
        case EET_GUI_EVENT :
              for (u32 i=0; i< guiEvents.size();i++)
                if (guiEvents[i] &&  guiEvents[i]->getGuiEventType() == event.GUIEvent.EventType  )
                   return guiEvents[i]->doEvent(event);
//            if (event.GUIEvent.EventType<guiEvents.size() && GuiEvents[event.GUIEvent.EventType])
//                return GuiEvents[event.GUIEvent.EventType]->doEvent(event);
            return false;
            break;
        case EET_LOG_TEXT_EVENT :
            if (LogEvent)
                return   LogEvent->doEvent(event);
            return false;
        case EET_USER_EVENT:
            if (UserEvent)
                return  UserEvent->doEvent(event);
            return false;
         case EET_JOYSTICK_INPUT_EVENT:
            if (JoyEvent)
                return  JoyEvent->doEvent(event);
            return false;
         case EGUIET_FORCE_32_BIT:
           DEBUG_PRINT("Error: CEventManager line 202\n");
         return false;
        }
        return false;
    }



    IEventEx* CEventManager::getEvent(EEVENT_TYPE t,gui::EGUI_EVENT_TYPE guitype,s32 startId)
    {
       _IRR_DEBUG_BREAK_IF(t==EGUIET_FORCE_32_BIT)
       switch(t)
        {
           case EET_KEY_INPUT_EVENT:
                 return  getKeyEvent(startId);
           case EET_MOUSE_INPUT_EVENT:
                 return  getMouseEvent(startId);
           case EET_LOG_TEXT_EVENT:
                 return  getLogEvent(startId);
           case EET_USER_EVENT:
                 return getUserEvent(startId);
           case EET_JOYSTICK_INPUT_EVENT:
                return getJoyEvent(startId);
           case EET_GUI_EVENT:
                return  getGuiEvent(guitype,startId);
           case EGUIET_FORCE_32_BIT:
           //DEBUG_PRINT("Error: CEventManager line 228\n");
           //return false;
           break;

        }
        DEBUG_PRINT("Error in EventManager line 233 ::getEvent; invalid Type\n");
        return 0;

    }

    IEventEx* CEventManager::getKeyEvent(s32 startId)
        {
            if (!KeyEvent) KeyEvent = new CKeyEventEx(startId);
            return KeyEvent;
        }

     IEventEx* CEventManager::getMouseEvent(s32 startId)
        {
            if (!MouseEvent) MouseEvent = new CMouseEventEx(startId);
            return MouseEvent;
        }


    IEventEx* CEventManager::getUserEvent(s32 startId)
    {
        if (!UserEvent)UserEvent = new CUserEventEx(startId);
        return UserEvent;

    }

    IEventEx* CEventManager::getLogEvent(s32 startId)
    {
        if (!LogEvent) LogEvent = new CLogEventEx(startId);
        return LogEvent;
    }

    IEventEx* CEventManager::getJoyEvent(s32 startId)
    {
        if (!JoyEvent) JoyEvent = new CJoyEventEx(startId);
        return JoyEvent;
    }


 IEventEx* CEventManager::getGuiEvent( gui::EGUI_EVENT_TYPE guitype,s32 start)
    {

        ///if this event is already created return it.
         for (u32 i=0; i< guiEvents.size();i++)
         {
             if (guiEvents[i] &&  guiEvents[i]->getGuiEventType() == guitype  )
                   return guiEvents[i];

         }

         ///need to create it
           CGuiEventEx* ex = new CGuiEventEx(guitype,start);
           if(ex)
           {
               guiEvents.push_back(ex);
               return ex;

           }

//      if(t < guiEvents.size())
//      {
//        if (!GuiEvents[t])
//            GuiEvents[t] = new CGuiEventEx(t,startId);
//        return GuiEvents[t];
//      }

      ///error
      DEBUG_PRINT("Error in EventManager::getGuiEvent\n EVENT_TYPE out of range. line 299\n");
      return 0;

    }


///mouse

    inline const core::position2d<s32>& CEventManager::mousePos()
    {
        return  msePos;
    }

    f32 CEventManager::mouseWheel()
    {
        if (wheelFlag)
        {
            wheelFlag =0;
            return wheel;
        }
        return  0;
    }
    inline bool CEventManager::mouseDown(EMOUSE_BUTTONS b)
    {
#ifdef _IRREVENTMANAGER_USEBITS
        return mouseKeys[b] & 1;
#else
        return mouseKeys[b];
#endif


    }

    inline bool CEventManager::mouseUp(EMOUSE_BUTTONS b)
    {
#ifdef _IRREVENTMANAGER_USEBITS
        return (bool)(mouseKeys[b] & (1<<4));
#else
    return ( b == lastMouseUp);
#endif


    }


//    inline bool* CEventManager::mouseData()
//    {
//        return mouseKeys;
//    }
#ifdef _IRREVENTMANAGER_USEBITS
          bool CEventManager::mouseClick(EMOUSE_BUTTONS b)
    {
        if (mouseKeys[b] & 1<<0)
        {
            if (! ( mouseKeys[b] & (1<<1) ) )
            {
                //mouseKeys[b] |= (1<<1);
                // mouseKeys[b] |= (1<<2);
                mouseKeys[b] |= 0x06;;//(3<<1);
                return  true;
            }
        }
        else
            mouseKeys[b] &= ~(1<<1);

        return (bool)(mouseKeys[b] & (1<<2));

    }
   #else
   bool CEventManager::mouseClick(EMOUSE_BUTTONS b)
    {
        if (mouseKeys[b])
        {
            if (!mouseKeysOld[b])
            {
                mouseKeysOld2[b] = mouseKeysOld[b] = true;
                //return  true;
            }
        }
        else
            mouseKeysOld[b] = false;

        return mouseKeysOld2[b];

    }



#endif
///keyboard
    inline bool CEventManager::keyDown(EKEY_CODE key)
    {
        #ifdef _IRREVENTMANAGER_USEBITS
         return  Keys[key] & 1;
        #else
         return  Keys[key];
        #endif
    }

    inline EKEY_CODE CEventManager::keyUp()
    {
        return (EKEY_CODE)lastKeyup;
    }

    s32  CEventManager::anyKeydown()
    {
        for (u32 i=0;i< irr::KEY_KEY_CODES_COUNT;i++)
            if (Keys[i] & 1)
                return true;
        return 0;

    }
 #ifdef _IRREVENTMANAGER_USEBITS
         ///returns true only once per key down!!!!!


    bool CEventManager::keyClick(EKEY_CODE key)
    {
        if (Keys[key] & 1<<0 )//keys[key])
        {
            if (! ( Keys[key] & (1<<1) ) )//keysOld[key])
            {
                //Keys[key] |= (1<<2); // 00100
                // Keys[key] |= (1<<1); // 00010
                Keys[key] |= 0x06;//(3<<1); //  011 == 3
                return true;
            }

        }
        else
            Keys[key] &= 0x0D ;//~(1<<1);// keysOld[key] = false; ~0010 = 1101 =0x0D

        return (bool)(Keys[key] & 0x04);//(1<<2)); // = 0100 = 0x04
    }

#else
    bool CEventManager::keyClick(EKEY_CODE key)
    {
        if (Keys[key])
        {
            if (!KeysOld[key])
             {
                   return  KeysOld2[key] = KeysOld[key] = true;
             }

        }
        else
            KeysOld[key] = false;

        return KeysOld2[key];
    }
 #endif


    wchar_t CEventManager::keyChar()
    {
        if (chr)
        {
            if (dt <= delay)
                dt++;
            if (dt==1 || dt > delay)
                return  chr;
        }
        return 0;
    }

    inline void CEventManager::setKeyDelay(u32 d)
    {
        delay = d;
    }

    inline u32  CEventManager::keyDelay()
    {
        return delay;
    }


    void CEventManager::update()
    {
#ifdef _IRREVENTMANAGER_USEBITS
        for (u32 i=0;i<3; i++)
        {
            /// 00100  ~ 11011
            /// 10000  ~ 01111
            /// 10100 0xA<1 ~ 01011

            //mouseKeys[i] &= ~(1<<2);
            //mouseKeys[i] &= ~(1<<4);
            //mouseKeys[i] &= ~(0xA<1);
            mouseKeys[i] &= 0xB;
        }
        lastKeyup   = 0;
        for (u32 i=0;i<irr::KEY_KEY_CODES_COUNT; i++)
            Keys[i] &= ~(1<<2);
#else
     memset(KeysOld2,0,KEY_KEY_CODES_COUNT);
     memset(mouseKeysOld2,0,3);
     lastKeyup   = 0;
     lastMouseUp =-1;


#endif

    }


    void CEventManager::removeAllReceiversByData(void* d)
    {
        if (UserEvent)  UserEvent->removeByData(d);
        if (LogEvent)   LogEvent->removeByData(d);
        if (MouseEvent) MouseEvent->removeByData(d);
        if (KeyEvent)   KeyEvent->removeByData(d);
        if (JoyEvent)   JoyEvent->removeByData(d);

        for (u32 i=0; i< guiEvents.size();i++)
            if (guiEvents[i])guiEvents[i]->removeByData(d);

    }

    void CEventManager::removeAllReceiversByObject(void* o)
    {
        if (UserEvent)  UserEvent->removeByObject(o);
        if (LogEvent)   LogEvent->removeByObject(o);
        if (MouseEvent) MouseEvent->removeByObject(o);
        if (KeyEvent)   KeyEvent->removeByObject(o);
        if (JoyEvent)   JoyEvent->removeByObject(o);

        for (u32 i=0; i< guiEvents.size();i++)
            if (guiEvents[i])guiEvents[i]->removeByObject(o);

    }


    void CEventManager::removeAllGuiReceiversByData(void* d)
    {
        for (u32 i=0; i< guiEvents.size();i++)
            if (guiEvents[i])guiEvents[i]->removeByData(d);
    }


    void CEventManager::removeAllGuiReceiversByCaller(void* s)
    {

        for (u32 i=0; i< guiEvents.size();i++)
            if (guiEvents[i]) guiEvents[i]->removeByCaller(s);

    }

    void CEventManager::removeAllGuiReceiversByObject(void* o)
    {

        for (u32 i=0; i< guiEvents.size();i++)
            if (guiEvents[i]) guiEvents[i]->removeByObject(o);

    }
    u32 CEventManager::getReceiverCount()
    {
        u32 cnt=0;
        for (u32 i=0; i< guiEvents.size();i++)
            if (guiEvents[i])
                cnt+=guiEvents[i]->getReceiverCount();
        if (UserEvent)  cnt+=UserEvent->getReceiverCount();
        if (LogEvent)   cnt+=LogEvent->getReceiverCount();
        if (MouseEvent) cnt+=MouseEvent->getReceiverCount();
        if (KeyEvent)   cnt+=KeyEvent->getReceiverCount();
        if (JoyEvent)   cnt+=JoyEvent->getReceiverCount();

        return cnt;

    }



}


